package cn.s1995.sloth.pay.biz.plugin.paypal;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.s1995.sloth.bizs.entity.Order;
import cn.s1995.sloth.bizs.entity.OrderItem;
import cn.s1995.sloth.common.core.util.StringUtil;
import cn.s1995.sloth.pay.entity.PayChannel;
import cn.s1995.sloth.pay.utils.PayConstants;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.paypal.PayPalApi;
import com.ijpay.paypal.PayPalApiConfig;
import com.ijpay.paypal.PayPalApiConfigKit;
import com.ijpay.paypal.accesstoken.AccessToken;
import com.ijpay.paypal.accesstoken.AccessTokenKit;
import com.ijpay.paypal.enums.PayPalApiUrl;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * PaypalApiPlus
 *
 * @author Lex
 * @description PaypalApi类扩展
 * @date 2024/01/06 03:32
 */
@Slf4j
public class PaypalApiPlus extends PayPalApi {

    /**
     * Webhook ID
     */
    public static final String WEBHOOK_ID = "8C8327343T727193D";

    /**
     * 验证Webhook签名接口
     */
    public static final String VERIFY_WEBHOOK_SIGNATURE = "/v1/notifications/verify-webhook-signature";

    public static void main(String[] args) {
        init();
        // IJPayHttpResponse response = queryOrder(PayPalApiConfigKit.getApiConfig(), "6HT75153UN870794S");
        // System.out.println(JSONUtil.toJsonStr(response.getBody()));
        // // {"id":"6HT75153UN870794S","intent":"CAPTURE","status":"COMPLETED","payment_source":{"paypal":{"email_address":"sb-5ihja29066429@personal.example.com","account_id":"6ZPXNAFHGWRCG","account_status":"VERIFIED","name":{"given_name":"Peng","surname":"Lex"},"phone_number":{"national_number":"4087987058"},"address":{"country_code":"US"}}},"purchase_units":[{"reference_id":"2024012005001","amount":{"currency_code":"USD","value":"10.00","breakdown":{"item_total":{"currency_code":"USD","value":"10.00"},"shipping":{"currency_code":"USD","value":"0.00"},"handling":{"currency_code":"USD","value":"0.00"},"insurance":{"currency_code":"USD","value":"0.00"},"shipping_discount":{"currency_code":"USD","value":"0.00"},"discount":{"currency_code":"USD","value":"0.00"}}},"payee":{"email_address":"sb-usv4d29079274@business.example.com","merchant_id":"RUSBAMRHEW9H2"},"description":"华为Mate60（遥遥领先）","invoice_id":"0574d755df254f029b9a41fa9ca5cb07","items":[{"name":"华为Mate60（遥遥领先）","unit_amount":{"currency_code":"USD","value":"10.00"},"tax":{"currency_code":"USD","value":"0.00"},"quantity":"1","description":"华为Mate60（遥遥领先）","sku":"1748619911450923010"}],"shipping":{"name":{"full_name":"Peng Lex"},"address":{"address_line_1":"1 Main St","admin_area_2":"San Jose","admin_area_1":"CA","postal_code":"95131","country_code":"US"}},"payments":{"captures":[{"id":"21R897433R3930113","status":"REFUNDED","amount":{"currency_code":"USD","value":"10.00"},"final_capture":true,"disbursement_mode":"INSTANT","seller_protection":{"status":"ELIGIBLE","dispute_categories":["ITEM_NOT_RECEIVED","UNAUTHORIZED_TRANSACTION"]},"seller_receivable_breakdown":{"gross_amount":{"currency_code":"USD","value":"10.00"},"paypal_fee":{"currency_code":"USD","value":"0.84"},"net_amount":{"currency_code":"USD","value":"9.16"}},"links":[{"href":"https://api.sandbox.paypal.com/v2/payments/captures/21R897433R3930113","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v2/payments/captures/21R897433R3930113/refund","rel":"refund","method":"POST"},{"href":"https://api.sandbox.paypal.com/v2/checkout/orders/6HT75153UN870794S","rel":"up","method":"GET"}],"create_time":"2024-01-20T09:35:59Z","update_time":"2024-01-22T10:27:10Z"}],"refunds":[{"id":"4W2910164P5098822","amount":{"currency_code":"USD","value":"10.00"},"note_to_payer":"order refund","seller_payable_breakdown":{"gross_amount":{"currency_code":"USD","value":"10.00"},"paypal_fee":{"currency_code":"USD","value":"0.00"},"platform_fees":[{"amount":{"currency_code":"USD","value":"0.00"}}],"net_amount":{"currency_code":"USD","value":"10.00"},"total_refunded_amount":{"currency_code":"USD","value":"10.00"}},"invoice_id":"0574d755df254f029b9a41fa9ca5cb07","status":"COMPLETED","links":[{"href":"https://api.sandbox.paypal.com/v2/payments/refunds/4W2910164P5098822","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v2/payments/captures/21R897433R3930113","rel":"up","method":"GET"}],"create_time":"2024-01-22T02:27:10-08:00","update_time":"2024-01-22T02:27:10-08:00"}]}}],"payer":{"name":{"given_name":"Peng","surname":"Lex"},"email_address":"sb-5ihja29066429@personal.example.com","payer_id":"6ZPXNAFHGWRCG","phone":{"phone_number":{"national_number":"4087987058"}},"address":{"country_code":"US"}},"update_time":"2024-01-22T10:27:10Z","links":[{"href":"https://api.sandbox.paypal.com/v2/checkout/orders/6HT75153UN870794S","rel":"self","method":"GET"}]}


        IJPayHttpResponse response = refund(PayPalApiConfigKit.getApiConfig(), 0L, "1JV25485S3420393G", new BigDecimal("1"));
        System.out.println(JSONUtil.toJsonStr(response.getBody()));
        // // {"id":"4W2910164P5098822","status":"COMPLETED","links":[{"href":"https://api.sandbox.paypal.com/v2/payments/refunds/4W2910164P5098822","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v2/payments/captures/21R897433R3930113","rel":"up","method":"GET"}]}
    }

    private static void init() {
        PayChannel channel = new PayChannel();
        channel.setId(1739467411189604354L);
        channel.setChannelId("PAYPAL_PAY");
        channel.setChannelName("PayPal支付");
        channel.setChannelMchId("5324122622596320283");
        channel.setReturnUrl("http://localhost:8088/mobile/");
        channel.setNotifyUrl("https://8286b06s52.goho.co");
        channel.setState("0");
        channel.setParam("""
                {
                  "clientId":"Af1IWtNliuIfyEX28FPiyo_54j681nFc7YOpYWG9e4Ldr8x_S-TOdN0ou4rUc5uUugk4xSK3TYjBUKr2",
                  "secret":"EF_OBPna7oX_IEKw4J2Pvxoe8i86qL67Mbzr9H92TJG3xDL4RumXnG-lYNk-SJjRwCX-gPqRxzuHZyC3",
                  "sandBox":true,
                  "domain":"https://test.api.king-of-panda.com",
                  "webhookId": "8C8327343T727193D"
                }
        """);
        channel.setRemark("Paypay支付");
        initPaypalConfig(channel);
    }


    /**
     * 初始化Paypal配置
     *
     * @param channel 支付渠道
     */
    public static void initPaypalConfig(PayChannel channel) {
        JSONObject params = JSONUtil.parseObj(channel.getParam());
        PaypalConfig config = new PaypalConfig();
        config.setClientId(params.getStr("clientId"));
        config.setSecret(params.getStr("secret"));
        config.setSandBox(params.getBool("sandBox"));
        config.setDomain(params.getStr("domain"));
        config.setWebhookId(params.getStr("webhookId"));
        PayPalApiConfigKit.setThreadLocalApiConfig(config);
    }


    /**
     * 创建订单（发起支付）
     *
     * @param apiConfig
     * @param orderList
     * @return IJPayHttpResponse
     * @see <a href="https://developer.paypal.com/docs/api/orders/v2/#orders_create">创建订单</a>
     */
    public static IJPayHttpResponse createOrder(PayPalApiConfig apiConfig, List<Order> orderList) {
        Map<String, Object> dataMap = new HashMap<>();
        // CAPTURE	立即发起付款
        dataMap.put("intent", "CAPTURE");

        dataMap.put("purchase_units", buildPurchaseUnits(orderList));

        Map<String, String> applicationContext = new HashMap<>();
        // applicationContext.put("return_url", ChannelPayApiConfigKit.get().getReturnUrl());
        // // // 将客户重定向到 PayPal 付款页面后，将显示“立即付款”按钮。如果在发起结账时知道最终金额，并且您希望在客户单击“立即付款”时立即处理付款，请使用此选项。
        // applicationContext.put("user_action", "PAY_NOW");
        // // applicationContext.put("cancel_url", "");

        dataMap.put("application_context", applicationContext);

        String data = JSONUtil.toJsonStr(dataMap);

        IJPayHttpResponse httpResponse = PaypalApiPlus.createOrder(apiConfig, data);
        log.info("createOrder request -  {}", data);
        log.info("createOrder response - {}", JSONUtil.toJsonStr(httpResponse));

        return httpResponse;
    }


    /**
     * 退款
     *
     * @param config        支付配置
     * @param refundId      业务退款id
     * @param orderId       订单id
     * @param captureId     捕获id(捕获支付的paypal生成的ID)
     * @param refundAmount  退款金额
     * @return
     */
    public static IJPayHttpResponse refund(PayPalApiConfig config, Long refundId, String captureId, BigDecimal refundAmount, Long... orderId) {
        String id = captureId.toString();

        Map<String, Object> map = new HashMap<>();
        map.put("custom_id", refundId.toString());
        if (ArrayUtil.isNotEmpty(orderId)) {
            map.put("invoice_id", orderId[0].toString());
        } else {
            map.put("invoice_id", "");
        }

        map.put("note_to_payer", "order refund");

        Map<String, String> amount = new HashMap<>();
        amount.put("value", refundAmount.toString());
        amount.put("currency_code", PayConstants.CURRENCY_USD);

        map.put("amount", amount);

        String data = JSONUtil.toJsonStr(map);
        IJPayHttpResponse response = refund(config, id, data);

        log.info("refund request -  {}", data);
        log.info("refund response - {}", JSONUtil.toJsonStr(response));

        return response;
    }

    /**
     * 验证Webhook签名
     *
     * @param request
     * @param params
     * @return
     */
    public static boolean verifyWebhookSign(PayPalApiConfig config, HttpServletRequest request, Map<String, Object> params) {
        // auth_algo从响应标头中存在的 PayPal-AUTH-ALGO 值中提取的。我验证了我的模拟 webhook 侦听器收到的响应标头中是否存在此值。
        // cert_url从响应标头中存在的 PayPal-CERT-URL 值中提取的。我验证了我的模拟 webhook 侦听器收到的响应标头中是否存在此值。
        // transmission_id从响应标头中存在的 PayPal-TRANSMISSION-ID 值中提取的。我验证了我的模拟 webhook 侦听器收到的响应标头中是否存在此值。
        // transmission_sig从响应标头中存在的 PayPal-TRANSMISSION-SIG 值中提取的。我验证了我的模拟 webhook 侦听器收到的响应标头中是否存在此值。
        // transmission_time从响应标头中存在的 PayPal-TRANSMISSION-TIME 值中提取的。我验证了我的模拟 webhook 侦听器收到的响应标头中是否存在此值。
        // webhook_id 在 PayPal Developer Portal 帐户中配置的 Webhook 的 ID。我验证了我的模拟 webhook 具有关联的 ID。
        // webhook_event - Webhook 事件通知。这包括从 PayPal 收到的通知响应，现在正在验证中。

        String webhookId = WEBHOOK_ID;
        if (config instanceof PaypalConfig paypalConfig) {
            if (StrUtil.isNotEmpty(paypalConfig.getWebhookId())) {
                webhookId = paypalConfig.getWebhookId();
            }
        }

        Map<String, Object> data = MapUtil.builder(new HashMap<String, Object>())
                .put("auth_algo", request.getHeader("PayPal-AUTH-ALGO"))
                .put("cert_url", request.getHeader("PayPal-CERT-URL"))
                .put("transmission_id", request.getHeader("PayPal-TRANSMISSION-ID"))
                .put("transmission_sig", request.getHeader("PayPal-TRANSMISSION-SIG"))
                .put("transmission_time", request.getHeader("PayPal-TRANSMISSION-TIME"))

                .put("webhook_id", webhookId)
                .put("webhook_event", params)
                // .put("webhook_event",
                //         MapUtil.builder(new HashMap<String, String>())
                //                 .put("event_version", params.get("event_version").toString())
                //                 .put("resource_version", params.get("resource_version").toString())
                //                 .build()
                // )
                .build();

        AccessToken accessToken = AccessTokenKit.get(config.getClientId());

        String dataStr = JSONUtil.toJsonStr(data);

        IJPayHttpResponse response = post(getReqUrl(VERIFY_WEBHOOK_SIGNATURE, config.isSandBox()), dataStr, getBaseHeaders(accessToken));

        log.info("verifyWebhookSign request - {}", dataStr);
        log.info("verifyWebhookSign response - {}", JSONUtil.toJsonStr(response));

        if (HttpServletResponse.SC_OK == response.getStatus()) {
            if ("SUCCESS".equals(JSONUtil.parseObj(response.getBody()).get("verification_status"))) {
                return true;
            } else {
                log.info("paypal webhook签名验证失败：{}", response.getBody());
            }
        }

        return false;
    }

    /**
     * 获取请求地址
     *
     * @param apiUrl
     * @param isSandBox
     * @return
     */
    private static String getReqUrl(String apiUrl, boolean isSandBox) {
        return (isSandBox ? PayPalApiUrl.SANDBOX_GATEWAY.getUrl() : PayPalApiUrl.LIVE_GATEWAY.getUrl()).concat(apiUrl);
    }


    /**
     * 构建支付单元
     *
     * @param orderList
     * @return
     */
    private static ArrayList<Map<String, Object>> buildPurchaseUnits(List<Order> orderList) {
        ArrayList<Map<String, Object>> purchaseUnits = new ArrayList<>();

        for (Order order : orderList) {
            Map<String, Object> amount = new HashMap<>();
            amount.put("currency_code", PayConstants.CURRENCY_USD);
            amount.put("value", order.getActualAmount().toString());

            BigDecimal orderItemTotalAmount = NumberUtil.add(order.getOrderItems().stream().map(OrderItem::getTotalAmount).toArray(BigDecimal[]::new));

            amount.put(
                    "breakdown",
                    MapUtil.builder()
                            .put(
                                    "item_total",
                                    MapUtil.builder()
                                            .put("currency_code", PayConstants.CURRENCY_USD)
                                            .put("value", orderItemTotalAmount.toString())
                                            .build()
                            )
                            .build()
            );


            Map<String, Object> itemMap = new HashMap<>();
            itemMap.put("amount", amount);
            itemMap.put("reference_id", order.getId().toString());
            itemMap.put("description", StringUtil.getValue(order.getRemark(), 127));
            // 自定义附加数据
            itemMap.put("custom_id", "");
            itemMap.put("invoice_id", "");
            itemMap.put("soft_descriptor", StringUtil.getValue(String.format("PAYPAL * %s", order.getUserNickname()), 22));

            // 产品数据
            ArrayList<Map<String, Object>> items = new ArrayList<>();
            for (OrderItem orderItem : order.getOrderItems()) {
                Map<String, Object> item = new HashMap<>();
                item.put("name", StringUtil.getValue(orderItem.getProdName(), 127));
                item.put("quantity", orderItem.getNum().toString());
                item.put("description", StringUtil.getValue(orderItem.getProdName(), 127));
                item.put("sku", StringUtil.getValue(orderItem.getSkuId().toString(), 127));
                item.put("category", "PHYSICAL_GOODS");
                // item.put("tax", "0");

                item.put(
                        "unit_amount",
                        MapUtil.builder()
                                .put("currency_code", PayConstants.CURRENCY_USD)
                                .put("value", orderItem.getAmount().toString())
                                .build()
                );

                items.add(item);
            }
            itemMap.put("items", items);
            purchaseUnits.add(itemMap);
        }

        return purchaseUnits;
    }

}
